broadway: Rewrite serials for clients
authorAlexander Larsson <alexl@redhat.com>
Tue, 8 Jan 2013 09:14:41 +0000 (10:14 +0100)
committerAlexander Larsson <alexl@redhat.com>
Tue, 8 Jan 2013 17:05:06 +0000 (18:05 +0100)
This seems right, but atm it breaks grabs.

gdk/broadway/broadway-server.c
gdk/broadway/broadway-server.h
gdk/broadway/broadwayd.c

index 05e3485944a167d8e1a5729e5ca1bba04bd51915..07652ef3aa56b406fe91e7ae5e3c42c65ed5a0cd 100644 (file)
@@ -253,13 +253,20 @@ process_input_messages (BroadwayServer *server)
        g_list_delete_link (server->input_messages,
                            server->input_messages);
 
+      if (message->base.serial == 0)
+       {
+         /* This was sent before we got any requests, but we don't want the
+            daemon serials to go backwards, so we fix it up to be the last used
+            serial */
+         message->base.serial = server->saved_serial - 1;
+       }
 
       update_event_state (server, message);
       client = -1;
       if (is_pointer_event (message) &&
          server->pointer_grab_window_id != -1)
        client = server->pointer_grab_client_id;
-         
+
       broadway_events_got_input (message, client);
       g_free (message);
     }
@@ -651,7 +658,7 @@ input_data_cb (GObject  *stream,
   return TRUE;
 }
 
-gulong
+guint32
 broadway_server_get_next_serial (BroadwayServer *server)
 {
   if (server->output)
index b4081398796958eec2bd094f7c23322b8d4d7290..e0a59c1fad6c8544f20808e135cb123e57028265 100644 (file)
@@ -24,7 +24,7 @@ BroadwayServer     *broadway_server_new                      (int
 gboolean            broadway_server_has_client               (BroadwayServer   *server);
 void                broadway_server_flush                    (BroadwayServer   *server);
 void                broadway_server_sync                     (BroadwayServer   *server);
-gulong              broadway_server_get_next_serial          (BroadwayServer   *server);
+guint32             broadway_server_get_next_serial          (BroadwayServer   *server);
 guint32             broadway_server_get_last_seen_time       (BroadwayServer   *server);
 gboolean            broadway_server_lookahead_event          (BroadwayServer   *server,
                                                              const char       *types);
index dce7b7f58ae81ceb19e80844adab670fa11310ff..ac100be6f4201c39c6f52fada1c4faf852a9a3cf 100644 (file)
@@ -17,11 +17,34 @@ GList *clients;
 
 static guint32 client_id_count = 1;
 
+/* Serials:
+ *
+ * Broadway tracks serials for all clients primarily to get the right behaviour wrt
+ * grabs. Each request the client sends gets an increasing per-client serial number, starting
+ * at 1. Thus, the client can now when a mouse event is seen whether the mouse event was
+ * sent before or after the server saw the grab request from the client (as this affects how
+ * the event is handled).
+ *
+ * There is only a single stream of increasing serials sent from the daemon to the web browser
+ * though, called "daemon serials", so we need to map back from the daemon serials to the client
+ * serials when we send an event to a client. So, each client keeps track of the mappings
+ * between its serials and daemon serials for any outstanding requests.
+ *
+ * There is some additional complexity in that there may be multiple consecutive web browser
+ * sessions, so we need to keep track of the last daemon serial used inbetween each web client
+ * connection so that the daemon serials can be strictly increasing.
+ */
+
+typedef struct {
+  guint32 client_serial;
+  guint32 daemon_serial;
+} BroadwaySerialMapping;
+
 typedef struct  {
   guint32 id;
   GSocketConnection *connection;
   GBufferedInputStream *in;
-  guint32 last_seen_serial;
+  GSList *serial_mappings;
   GList *windows;
   guint disconnect_idle;
 } BroadwayClient;
@@ -34,6 +57,7 @@ client_free (BroadwayClient *client)
   clients = g_list_remove (clients, client);
   g_object_unref (client->connection);
   g_object_unref (client->in);
+  g_slist_free_full (client->serial_mappings, g_free);
   g_free (client);
 }
 
@@ -172,6 +196,70 @@ open_surface (char *name, int width, int height)
   return surface;
 }
 
+void
+add_client_serial_mapping (BroadwayClient *client,
+                          guint32 client_serial,
+                          guint32 daemon_serial)
+{
+  BroadwaySerialMapping *map;
+  GSList *last;
+
+  last = g_slist_last (client->serial_mappings);
+
+  if (last != NULL)
+    {
+      map = last->data;
+
+      /* If we have no web client, don't grow forever */
+      if (map->daemon_serial == daemon_serial)
+       {
+         map->client_serial = client_serial;
+         return;
+       }
+    }
+
+  map = g_new0 (BroadwaySerialMapping, 1);
+  map->client_serial = client_serial;
+  map->daemon_serial = daemon_serial;
+  client->serial_mappings = g_slist_append (client->serial_mappings, map);
+}
+
+/* Returns the latest seen client serial at the time we sent
+   a daemon request to the browser with a specific daemon serial */
+guint32
+get_client_serial (BroadwayClient *client, guint32 daemon_serial)
+{
+  BroadwaySerialMapping *map;
+  GSList *l, *found;
+  guint32 client_serial = 0;
+
+  found = NULL;
+  for (l = client->serial_mappings;  l != NULL; l = l->next)
+    {
+      map = l->data;
+
+      if (map->daemon_serial <= daemon_serial)
+       {
+         found = l;
+         client_serial = map->client_serial;
+       }
+      else
+       break;
+    }
+
+  /* Remove mappings before the found one, they will never more be used */
+  while (found != NULL &&
+        client->serial_mappings != found)
+    {
+      g_free (client->serial_mappings->data);
+      client->serial_mappings =
+       g_slist_delete_link (client->serial_mappings, client->serial_mappings);
+    }
+
+  return client_serial;
+}
+
+
 static void
 client_handle_request (BroadwayClient *client,
                       BroadwayRequest *request)
@@ -183,8 +271,9 @@ client_handle_request (BroadwayClient *client,
   BroadwayReplyUngrabPointer reply_ungrab_pointer;
   cairo_region_t *area;
   cairo_surface_t *surface;
+  guint32 before_serial, now_serial;
 
-  client->last_seen_serial = request->base.serial;
+  before_serial = broadway_server_get_next_serial (server);
 
   switch (request->base.type)
     {
@@ -199,7 +288,7 @@ client_handle_request (BroadwayClient *client,
       client->windows =
        g_list_prepend (client->windows,
                        GUINT_TO_POINTER (reply_new_window.id));
-      
+
       send_reply (client, request, (BroadwayReply *)&reply_new_window, sizeof (reply_new_window),
                  BROADWAY_REPLY_NEW_WINDOW);
       break;
@@ -291,6 +380,20 @@ client_handle_request (BroadwayClient *client,
     default:
       g_warning ("Unknown request of type %d\n", request->base.type);
     }
+
+
+  now_serial = broadway_server_get_next_serial (server);
+
+  /* If we sent a new output request, map that this client serial to that, otherwise
+     update old mapping for previously sent daemon serial */
+  if (now_serial != before_serial)
+    add_client_serial_mapping (client,
+                              request->base.serial,
+                              before_serial);
+  else
+    add_client_serial_mapping (client,
+                              request->base.serial,
+                              before_serial - 1);
 }
 
 static void
@@ -494,9 +597,12 @@ broadway_events_got_input (BroadwayInputMsg *message,
   GList *l;
   BroadwayReplyEvent reply_event;
   gsize size;
+  guint32 daemon_serial;
 
   size = get_event_size (message->base.type);
 
+  daemon_serial = message->base.serial;
+
   reply_event.msg = *message;
 
   for (l = clients; l != NULL; l = l->next)
@@ -506,6 +612,8 @@ broadway_events_got_input (BroadwayInputMsg *message,
       if (client_id == -1 ||
          client->id == client_id)
        {
+         message->base.serial = get_client_serial (client, daemon_serial);
+
          send_reply (client, NULL, (BroadwayReply *)&reply_event,
                      sizeof (BroadwayReplyBase) + size,
                      BROADWAY_REPLY_EVENT);